Skip to content

Dump descriptors from a file#246

Open
ribalda wants to merge 2 commits into
gregkh:masterfrom
ribalda:f-flag
Open

Dump descriptors from a file#246
ribalda wants to merge 2 commits into
gregkh:masterfrom
ribalda:f-flag

Conversation

@ribalda
Copy link
Copy Markdown

@ribalda ribalda commented Mar 12, 2026

Add a new flag that allows dumping a USB descriptor file in a human-readable
format. This is particularly useful for analyzing descriptor dumps collected
from systems where lsusb cannot be run directly, such as during hardware
bring-up or on non-Linux platforms.

The descriptor file is typically obtained from:
/sys/bus/usb/devices/[device-bus-id]/descriptors

The normal workflow will be something like:
HOST # scp target:/sys/bus/usb/devices/X-X/descriptors .
HOST # lsusb -F descriptors

@gregkh
Copy link
Copy Markdown
Owner

gregkh commented Mar 12, 2026

oh, i've wanted this for a while, thanks! let me run some basic tests and then look closer at this next week.

Comment thread desc-file.c
cfg->MaxPower = buf[8];

if (cfg->bNumInterfaces > 0) {
cfg->interface = calloc(cfg->bNumInterfaces, sizeof(*cfg->interface));

Check failure

Code scanning / CodeQL

Uncontrolled allocation size

This allocation size is derived from [user input (buffer read by read)](1) and could allocate arbitrary amounts of memory. This allocation size is derived from [user input (buffer read by read)](2) and could allocate arbitrary amounts of memory.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cfg->bNumInterfaces = buf[4];

The value of bNumInterfaces is capped to 255 because it comes from a single byte

Comment thread desc-file.c
alt->bInterfaceProtocol = buf[7];
alt->iInterface = buf[8];
if (alt->bNumEndpoints > 0) {
alt->endpoint = calloc(alt->bNumEndpoints, sizeof(*alt->endpoint));

Check failure

Code scanning / CodeQL

Uncontrolled allocation size

This allocation size is derived from [user input (buffer read by read)](1) and could allocate arbitrary amounts of memory. This allocation size is derived from [user input (buffer read by read)](2) and could allocate arbitrary amounts of memory.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as before

alt->bNumEndpoints = buf[4];

Comment thread desc-file.c
*
* Returns a pointer to the allocated configuration descriptor, or NULL on failure.
*/
static struct libusb_config_descriptor *parse_config_descriptor(const unsigned char *buf, int size)

Check warning

Code scanning / CodeQL

Poorly documented large function

Poorly documented function: fewer than 2% comments for a function of 143 lines.
Comment thread desc-file.c
Comment on lines +71 to +160
switch (type) {
case LIBUSB_DT_INTERFACE:
if (buf[2] >= cfg->bNumInterfaces)
break;
ifc = (struct libusb_interface *)&cfg->interface[buf[2]];
ifc->num_altsetting++;
tmp = realloc((void *)ifc->altsetting, ifc->num_altsetting * sizeof(*ifc->altsetting));
if (!tmp) {
perror("realloc");
desc_file_free_config_descriptor(cfg);
return NULL;
}
ifc->altsetting = tmp;
alt = (struct libusb_interface_descriptor *)&ifc->altsetting[ifc->num_altsetting - 1];
memset(alt, 0, sizeof(*alt));
alt->bLength = buf[0];
alt->bDescriptorType = buf[1];
alt->bInterfaceNumber = buf[2];
alt->bAlternateSetting = buf[3];
alt->bNumEndpoints = buf[4];
alt->bInterfaceClass = buf[5];
alt->bInterfaceSubClass = buf[6];
alt->bInterfaceProtocol = buf[7];
alt->iInterface = buf[8];
if (alt->bNumEndpoints > 0) {
alt->endpoint = calloc(alt->bNumEndpoints, sizeof(*alt->endpoint));
if (!alt->endpoint) {
perror("calloc");
desc_file_free_config_descriptor(cfg);
return NULL;
}
}
ep = NULL;
break;

case LIBUSB_DT_ENDPOINT:
if (!(alt && alt->endpoint))
break;
ep_idx = 0;
/* Find next free endpoint slot */
while (ep_idx < alt->bNumEndpoints && alt->endpoint[ep_idx].bLength)
ep_idx++;
if (ep_idx < alt->bNumEndpoints) {
ep = (struct libusb_endpoint_descriptor *)&alt->endpoint[ep_idx];
ep->bLength = buf[0];
ep->bDescriptorType = buf[1];
ep->bEndpointAddress = buf[2];
ep->bmAttributes = buf[3];
ep->wMaxPacketSize = buf[4] | (buf[5] << 8);
ep->bInterval = buf[6];
ep->bRefresh = (len > 7) ? buf[7] : 0;
ep->bSynchAddress = (len > 8) ? buf[8] : 0;
}
break;

default:
/* Extra descriptors */
if (ep) {
tmp = realloc((void *)ep->extra, ep->extra_length + len);
if (!tmp) {
perror("realloc");
desc_file_free_config_descriptor(cfg);
return NULL;
}
ep->extra = tmp;
memcpy((void *)(ep->extra + ep->extra_length), buf, len);
ep->extra_length += len;
} else if (alt) {
tmp = realloc((void *)alt->extra, alt->extra_length + len);
if (!tmp) {
perror("realloc");
desc_file_free_config_descriptor(cfg);
return NULL;
}
alt->extra = tmp;
memcpy((void *)(alt->extra + alt->extra_length), buf, len);
alt->extra_length += len;
} else if (cfg) {
tmp = realloc((void *)cfg->extra, cfg->extra_length + len);
if (!tmp) {
perror("realloc");
desc_file_free_config_descriptor(cfg);
return NULL;
}
cfg->extra = tmp;
memcpy((void *)(cfg->extra + cfg->extra_length), buf, len);
cfg->extra_length += len;
}
break;
}

Check notice

Code scanning / CodeQL

Long switch case

Switch has at least one case that is too long: [LIBUSB_DT_INTERFACE (33 lines)](1).
Comment thread desc-file.c
(struct libusb_endpoint_descriptor *)&alt->endpoint[k];
if (!ep)
continue;
if (ep->extra)

Check notice

Code scanning / CodeQL

Guarded Free

unnecessary NULL check before call to [free](1)
Comment thread desc-file.c
if (ep->extra)
free((void *)ep->extra);
}
if (alt->endpoint)

Check notice

Code scanning / CodeQL

Guarded Free

unnecessary NULL check before call to [free](1)
Comment thread desc-file.c
}
if (alt->endpoint)
free((void *)alt->endpoint);
if (alt->extra)

Check notice

Code scanning / CodeQL

Guarded Free

unnecessary NULL check before call to [free](1)
Comment thread desc-file.c
if (alt->extra)
free((void *)alt->extra);
}
if (ifc->altsetting)

Check notice

Code scanning / CodeQL

Guarded Free

unnecessary NULL check before call to [free](1)
Comment thread desc-file.c
if (ifc->altsetting)
free((void *)ifc->altsetting);
}
if (cfg->interface)

Check notice

Code scanning / CodeQL

Guarded Free

unnecessary NULL check before call to [free](1)
Comment thread desc-file.c
}
if (cfg->interface)
free((void *)cfg->interface);
if (cfg->extra)

Check notice

Code scanning / CodeQL

Guarded Free

unnecessary NULL check before call to [free](1)
ribalda added 2 commits March 12, 2026 21:36
All existing callers of get_vendor_product_with_fallback() already have
the device descriptor available. By passing it directly, we avoid
an unnecessary call to libusb_get_device_descriptor() inside the
function.

Signed-off-by: Ricardo Ribalda <ribalda@google.com>
Add a new flag that allows dumping a USB descriptor file in a human-readable
format. This is particularly useful for analyzing descriptor dumps collected
from systems where lsusb cannot be run directly, such as during hardware
bring-up or on non-Linux platforms.

The descriptor file is typically obtained from:
/sys/bus/usb/devices/[device-bus-id]/descriptors

The normal workflow will be something like:
HOST # scp target:/sys/bus/usb/devices/X-X/descriptors .
HOST # lsusb -F descriptors

Signed-off-by: Ricardo Ribalda <ribalda@google.com>
@ribalda
Copy link
Copy Markdown
Author

ribalda commented May 27, 2026

Hi Greg, did you have to look into this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants